home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / asm / asmtutr.exe / ASMTUTOR.TXT < prev   
Text File  |  1991-10-29  |  75KB  |  1,678 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.                     IBM Personal Computer Assembly
  8.                          Language Tutorial
  9.  
  10.  
  11.  
  12.                           Joshua Auerbach
  13.                           Yale University
  14.                         Yale Computer Center
  15.                          175 Whitney Avenue
  16.                            P. O. Box 2112
  17.                      New Haven, Connecticut 06520
  18.  
  19.                          Installation Code YU
  20.  
  21.                  Integrated Personal Computers Project
  22.                         Communications Group
  23.                  Communications and Data Base Division
  24.  
  25.                             Session C316
  26.  
  27.  
  28.  
  29.  
  30. This talk is for people who are just getting started with the PC MACRO
  31. Assembler.  Maybe you are just contemplating doing some coding in
  32. assembler, maybe you have tried it with mixed success.  If you are here to
  33. get aimed in the right direction, to get off to a good start with the
  34. assembler, then you have come for the right reason.  I can't promise you'll
  35. get what you want, but I'll do my best.
  36.  
  37. On the other hand, if you have already turned out some working assembler
  38. code, then this talk is likely to be on the elementary side for you.  If
  39. you want to review a few basics and have no where else pressing to go, then
  40. by all means stay.
  41.  
  42.  
  43.  
  44. Why Learn Assembler?
  45. ____________________
  46.  
  47.  
  48. The reasons for LEARNING assembler are not the same as the reasons for
  49. USING it in a particular application.  But, we have to start with some of
  50. the reasons for using it and then I think the reasons for learning it will
  51. become clear.
  52.  
  53. First, let's dispose of a bad reason for using it.  Don't use it just
  54. because you think it is going to execute faster.  A particular sequence of
  55. ordinary bread-and-butter computations written in PASCAL, C, FORTRAN, or
  56. compiled BASIC can do the job just about as fast as the same algorithm
  57. coded in assembler.  Of course, interpretive BASIC is slower, but if you
  58. have a BASIC application which runs too slow you probably want to try com-
  59.  
  60.  
  61. IBM PC Assembly Language Tutorial                                         1
  62.  
  63.  
  64. piling it before you think too much about translating parts of it to
  65. another language.
  66.  
  67. On the other hand, high level languages do tend to isolate you from the
  68. machine.  That is both their strength and their weakness.  Usually, when
  69. implemented on a micro, a high level language provides an escape mechanism
  70. to the underlying operating system or to the bare machine.  So, for
  71. example, BASIC has its PEEK and POKE.  But, the route to the bare machine
  72. is often a circuitous one, leading to tricky programming which is hard to
  73. follow.
  74.  
  75. For those of us working on PC's connected to SHARE-class mainframes, we are
  76. generally concerned with three interfaces:  the keyboard, the screen, and
  77. the communication line or lines.  All three of these entities raise machine
  78. dependent issues which are imperfectly addressed by the underlying operat-
  79. ing system or by high level languages.
  80.  
  81. Sometimes, the system or the language does too little for you.  For
  82. example, with the asynch adapter, the system provides no interrupt handler,
  83. no buffer, and no flow control.  The application is stuck with the respon-
  84. sibility for monitoring that port and not missing any characters, then
  85. deciding what to do with all errors.  BASIC does a reasonable job on some
  86. of this, but that is only BASIC.  Most other languages do less.
  87.  
  88. Sometimes, the system may do too much for you.  System support for the key-
  89. board is an example.  At the hardware level, all 83 keys on the keyboard
  90. send unique codes when they are pressed, held down, and released.  But,
  91. someone has decided that certain keys, like Num Lock and Scroll Lock are
  92. going to do certain things before the application even sees them and can't
  93. therefore be used as ordinary keys.
  94.  
  95. Sometimes, the system does about the right amount of stuff but does it less
  96. efficiently then it should.  System support for the screen is in this
  97. class.  If you use only the official interface to the screen you sometimes
  98. slow your application down unacceptably.  I said before, don't use assem-
  99. bler just to speed things up, but there I was talking about mainline code,
  100. which generally can't be speeded up much by assembler coding.  A critical
  101. system interface is a different matter:  sometimes we may have to use
  102. assembler to bypass a hopelessly inefficient implementation.  We don't want
  103. to do this if we can avoid it, but sometimes we can't.
  104.  
  105. Assembly language code can overcome these deficiencies.  In some cases, you
  106. can also overcome these deficiencies by judicious use of the escape valves
  107. which your high level language provides.  In BASIC, you can PEEK and POKE
  108. and INP and OUT your way around a great many issues.  In many other lan-
  109. guages you can issue system calls and interrupts and usually manage, one
  110. way or other, to modify system memory.  Writing handlers to take real-time
  111. hardware interrupts from the keyboard or asynch port, though, is still
  112. going to be a problem in most languages.  Some languages claim to let you
  113. do it but I have yet to see an acceptably clean implementation done that
  114. way.
  115.  
  116. The real reason while assembler is better than "tricky POKEs" for writing
  117. machine-dependent code, though, is the same reason why PASCAL is better
  118. than assembler for writing a payroll package:  it is easier to maintain.
  119.  
  120.  
  121. IBM PC Assembly Language Tutorial                                         2
  122.  
  123.  
  124. Let the high level language do what it does best, but recognize that there
  125. are some things which are best done in assembler code.  The assembler,
  126. unlike the tricky POKE, can make judicious use of equates, macros, labels,
  127. and appropriately placed comments to show what is really going on in this
  128. machine-dependent realm where it thrives.
  129.  
  130. So, there are times when it becomes appropriate to write in assembler; giv-
  131. en that, if you are a responsible programmer or manager, you will want to
  132. be "assembler-literate" so you can decide when assembler code should be
  133. written.
  134.  
  135. What do I mean by "assembler-literate?"  I don't just mean understanding
  136. the 8086 architecture; I think, even if you don't write much assembler code
  137. yourself, you ought to understand the actual process of turning out assem-
  138. bler code and the various ways to incorporate it into an application.  You
  139. ought to be able to tell good assembler code from bad, and appropriate
  140. assembler code from inappropriate.
  141.  
  142.  
  143.  
  144. Steps to becoming ASSEMBLER-LITERATE
  145. ____________________________________
  146.  
  147.  
  148. 1.  Learn the 8086 architecture and most of the instruction set.  Learn
  149.     what you need to know and ignore what you don't.  Reading:  The 8086
  150.     Primer by Stephen Morse, published by Hayden.  You need to read only
  151.     two chapters, the one on machine organization and the one on the
  152.     instruction set.
  153.  
  154. 2.  Learn about a few simple DOS function calls.  Know what services the
  155.     operating system provides.  If appropriate, learn a little about other
  156.     systems too.  It will aid portability later on.  Reading:  appendices D
  157.     and E of the PC DOS manual.
  158.  
  159. 3.  Learn enough about the MACRO assembler and the LINKer to write some
  160.     simple things that really work.  Here, too, the main thing is figuring
  161.     out what you don't need to know.  Whatever you do, don't study the sam-
  162.     ple programs distributed with the assembler unless you have nothing
  163.     better!
  164.  
  165. 4.  At the same time as you are learning the assembler itself, you will
  166.     need to learn a few tools and concepts to properly combine your assem-
  167.     bler code with the other things you do.  If you plan to call assembler
  168.     subroutines from a high level language, you will need to study the
  169.     interface notes provided in your language manual.  Usually, this forms
  170.     an appendix of some sort.  If you plan to package your assembler rou-
  171.     tines as .COM programs you will need to learn to do this.  You should
  172.     also learn to use DEBUG.
  173.  
  174. 5.  Read the Technical Reference, but very selectively.  The most important
  175.     things to know are the header comments in the BIOS listing.  Next, you
  176.     will want to learn about the RS 232 port and maybe about the video
  177.     adapters.
  178.  
  179.  
  180.  
  181.  
  182. IBM PC Assembly Language Tutorial                                         3
  183.  
  184.  
  185. Notice that the key thing in all five phases is being selective.  It is
  186. easy to conclude that there is too much to learn unless you can throw away
  187. what you don't need.  Most of the rest of this talk is going to deal with
  188. this very important question of what you need and don't need to learn in
  189. each phase.  In some cases, I will have to leave you to do almost all of
  190. the learning, in others, I will teach a few salient points, enough, I hope,
  191. to get you started.  I hope you understand that all I can do in an hour is
  192. get you started on the way.
  193.  
  194.  
  195.  
  196. Phase 1:  Learn the architecture and instruction set
  197. ____________________________________________________
  198.  
  199.  
  200. The Morse book might seem like a lot of book to buy for just two really
  201. important chapters; other books devote a lot more space to the instruction
  202. set and give you a big beautiful reference page on each instruction.  And,
  203. some of the other things in the Morse book, although interesting, really
  204. aren't very vital and are covered too sketchily to be of any real help.
  205. The reason I like the Morse book is that you can just read it; it has a
  206. very conversational style, it is very lucid, it tells you what you really
  207. need to know, and a little bit more which is by way of background; because
  208. nothing really gets belabored to much, you can gracefully forget the things
  209. you don't use.  And, I very much recommend READING Morse rather than study-
  210. ing it.  Get the big picture at this point.
  211.  
  212. Now, you want to concentrate on those things which are worth fixing in mem-
  213. ory.  After you read Morse, you should relate what you have learned to this
  214. outline.
  215.  
  216. 1.  You want to fix in your mind the idea of the four segment registers
  217.     CODE, DATA, STACK, and EXTRA.  This part is pretty easy to grasp.  The
  218.     8086 and the 8088 use 20 bit addresses for memory, meaning that they
  219.     can address up to 1 megabyte of memory.  But, the registers and the
  220.     address fields in all the instructions are no more that 16 bits long.
  221.     So, how to address all of that memory?  Their solution is to put
  222.     together two 16 bit quantities like this:
  223.  
  224.       calculation  SSSS0   ---- value in the relevant segment register SHL 4
  225.       depicted in   AAAA   ---- apparent address from register or instruction
  226.       hexadecimal --------
  227.                    RRRRR   ---- real address placed on address bus
  228.  
  229.     In other words, any time memory is accessed, your program will supply a
  230.     sixteen bit address.  Another sixteen bit address is acquired from a
  231.     segment register, left shifted four bits (one nibble) and added to it
  232.     to form the real address.  You can control the values in the segment
  233.     registers and thus access any part of memory you want.  But the segment
  234.     registers are specialized:  one for code, one for most data accesses,
  235.     one for the stack (which we'll mention again) and one "extra" one for
  236.     additional data accesses.
  237.  
  238.     Most people, when they first learn about this addressing scheme become
  239.     obsessed with converting everything to real 20 bit addresses.  After a
  240.     while, though, you get use to thinking in segment/offset form.  You
  241.  
  242.  
  243. IBM PC Assembly Language Tutorial                                         4
  244.  
  245.  
  246.     tend to get your segment registers set up at the beginning of the pro-
  247.     gram, change them as little as possible, and think just in terms of
  248.     symbolic locations in your program, as with any assembly language.
  249.  
  250.       EXAMPLE:
  251.            MOV  AX,DATASEG
  252.            MOV  DS,AX          ;Set value of Data segment
  253.            ASSUME DS:DATASEG   ;Tell assembler DS is usable
  254.            .......
  255.            MOV  AX,PLACE       ;Access storage symbolically by 16 bit address
  256.  
  257.     In the above example, the assembler knows that no special issues are
  258.     involved because the machine generally uses the DS register to complete
  259.     a normal data reference.
  260.  
  261.     If you had used ES instead of DS in the above example, the assembler
  262.     would have known what to do, also.  In front of the MOV instruction
  263.     which accessed the location PLACE, it would have placed the ES segment
  264.     prefix.  This would tell the machine that ES should be used, instead of
  265.     DS, to complete the address.
  266.  
  267.     Some conventions make it especially easy to forget about segment regis-
  268.     ters.  For example, any program of the COM type gets control with all
  269.     four segment registers containing the same value.  This program exe-
  270.     cutes in a simplified 64K address space.  You can go outside this
  271.     address space if you want but you don't have to.
  272.  
  273. 2.  You will want to learn what other registers are available and learn
  274.     their personalities:
  275.  
  276.         AX and DX are general purpose registers.  They become special only
  277.         when accessing machine and system interfaces.
  278.  
  279.         CX is a general purpose register which is slightly specialized for
  280.         counting.
  281.  
  282.         BX is a general purpose register which is slightly specialized for
  283.         forming base-displacement addresses.
  284.  
  285.         AX-DX can be divided in half, forming AH, AL, BH, BL, CH, CL, DH,
  286.         DL.
  287.  
  288.         SI and DI are strictly 16 bit.  They can be used to form indexed
  289.         addresses (like BX) and they are also used to point to strings.
  290.  
  291.         SP is hardly ever manipulated.  It is there to provide a stack.
  292.  
  293.         BP is a manipulable cousin to SP.  Use it to access data which has
  294.         been pushed onto the stack.
  295.  
  296.         Most sixteen bit operations are legal (even if unusual) when per-
  297.         formed in SI, DI, SP, or BP.
  298.  
  299.  
  300.  
  301.  
  302.  
  303. IBM PC Assembly Language Tutorial                                         5
  304.  
  305.  
  306. 3.  You will want to learn the classifications of operations available
  307.     WITHOUT getting hung up in the details of how 8086 opcodes are con-
  308.     structed.
  309.  
  310.     8086 opcodes are complex.  Fortunately, the assembler opcodes used to
  311.     assemble them are simple.  When you read a book like Morse, you will
  312.     learn some things which are worth knowing but NOT worth dwelling on.
  313.  
  314.     a.  8086 and 8088 instructions can be broken up into subfields and bits
  315.         with names like R/M, MOD, S and W. These parts of the instruction
  316.         modify the basic operation in such ways as whether it is 8 bit or
  317.         16 bit, if 16 bit, whether all 16 bits of the data are given,
  318.         whether the instruction is register to register, register to
  319.         memory, or memory to register, for operands which are registers,
  320.         which register, for operands which are memory, what base and index
  321.         registers should be used in finding the data.
  322.  
  323.     b.  Also, some instructions are actually represented by several differ-
  324.         ent machine opcodes depending on whether they deal with immediate
  325.         data or not, or on other issues, and there are some expedited forms
  326.         which assume that one of the arguments is the most commonly used
  327.         operand, like AX in the case of arithmetic.
  328.  
  329.     There is no point in memorizing any of this detail; just distill the
  330.     bottom line, which is, what kinds of operand combinations EXIST in the
  331.     instruction set and what kinds don't.  If you ask the assembler to ADD
  332.     two things and the two things are things for which there is a legal ADD
  333.     instruction somewhere in the instruction set, the assembler will find
  334.     the right instruction and fill in all the modifier fields for you.
  335.  
  336.     I guess if you memorized all the opcode construction rules you might
  337.     have a crack at being able to disassemble hex dumps by eye, like you
  338.     may have learned to do somewhat with 370 assembler.  I submit to you
  339.     that this feat, if ever mastered by anyone, would be in the same class
  340.     as playing the "Minute Waltz" in a minute; a curiosity only.
  341.  
  342.     Here is the basic matrix you should remember:
  343.  
  344.  
  345.  
  346.  
  347.  
  348.  
  349.  
  350.  
  351.  
  352.  
  353.  
  354.  
  355.  
  356.  
  357.  
  358.  
  359.  
  360.  
  361.  
  362.  
  363. IBM PC Assembly Language Tutorial                                         6
  364.  
  365.  
  366.          Two operands:          One operand:
  367.           R <-- M                R
  368.           M <-- R                M
  369.           R <-- R                S *
  370.           R|M <-- I
  371.           R|M <-- S  *
  372.           S <-- R|M  *
  373.  
  374.       * -- data moving instructions (MOV, PUSH, POP) only
  375.       S -- segment register (CS, DS, ES, SS)
  376.       R -- ordinary register (AX, BX, CX, DX, SI, DI, BP, SP,
  377.                               AH, AL, BH, BL, CH, CL, DH, DL)
  378.       M -- one of the following
  379.                pure address
  380.                [BX]+offset
  381.                [BP]+offset
  382.                any of the above indexed by SI
  383.                any of the first three indexed by DI
  384.  
  385. 4.  Of course, you want to learn the operations themselves.  As I've sug-
  386.     gested, you want to learn the op codes as the assembler presents them,
  387.     not as the CPU machine language presents them.  So, even though there
  388.     are many MOV op codes you don't need to learn them.  Basically, here is
  389.     the instruction set:
  390.  
  391.     a.  Ordinary two operand instructions.  These instructions perform an
  392.         operation and leave the result in place of one of the operands.
  393.         They are
  394.  
  395.         1)  ADD and ADC -- addition, with or without including a carry from
  396.             a previous addition
  397.  
  398.         2)  SUB and SBB -- subtraction, with or without including a borrow
  399.             from a previous subtraction
  400.  
  401.         3)  CMP -- compare.  It is useful to think of this as a subtraction
  402.             with the answer being thrown away and neither operand actually
  403.             changed
  404.  
  405.         4)  AND, OR, XOR -- typical boolean operations
  406.  
  407.         5)  TEST -- like an AND, except the answer is thrown away and nei-
  408.             ther operand is changed.
  409.  
  410.         6)  MOV -- move data from source to target
  411.  
  412.         7)  LDS, LES, LEA -- some specialized forms of MOV with side
  413.             effects
  414.  
  415.     b.  Ordinary one operand instructions.  These can take any of the oper-
  416.         and forms described above.  Usually, the perform the operation and
  417.         leave the result in the stated place:
  418.  
  419.         1)  INC -- increment contents
  420.  
  421.  
  422.  
  423. IBM PC Assembly Language Tutorial                                         7
  424.  
  425.  
  426.         2)  DEC -- decrement contents
  427.  
  428.         3)  NEG -- twos complement
  429.  
  430.         4)  NOT -- ones complement
  431.  
  432.         5)  PUSH -- value goes on stack (operand location itself unchanged)
  433.  
  434.         6)  POP -- value taken from stack, replaces current value
  435.  
  436.     c.  Now you touch on some instructions which do not follow the general
  437.         operand rules but which require the use of certain registers.  The
  438.         important ones are
  439.  
  440.         1)  The multiply and divide instructions
  441.  
  442.         2)  The "adjust" instructions which help in performing arithmetic
  443.             on ASCII or packed decimal data
  444.  
  445.         3)  The shift and rotate instructions.  These have a restriction on
  446.             the second operand:  it must either be the immediate value 1 or
  447.             the contents of the CL register.
  448.  
  449.         4)  IN and OUT which send or receive data from one of the 1024
  450.             hardware ports.
  451.  
  452.         5)  CBW and CWD -- convert byte to word or word to doubleword by
  453.             sign extension
  454.  
  455.     d.  Flow of control instructions.  These deserve study in themselves
  456.         and we will discuss them a little more.  They include
  457.  
  458.         1)  CALL, RET -- call and return
  459.  
  460.         2)  INT, IRET -- interrupt and return-from-interrupt
  461.  
  462.         3)  JMP -- jump or "branch"
  463.  
  464.         4)  LOOP, LOOPNZ, LOOPZ -- special (and useful) instructions which
  465.             implement a counted loop similar to the 370 BCT instruction
  466.  
  467.         5)  various conditional jump instructions
  468.  
  469.     e.  String instructions.  These implement a limited storage-to-storage
  470.         instruction subset and are quite powerful.  All of them have the
  471.         property that
  472.  
  473.         1)  The source of data is described by the combination DS and SI.
  474.  
  475.         2)  The destination of data is described by the combination ES and
  476.             DI.
  477.  
  478.         3)  As part of the operation, the SI and/or DI register(s) is(are)
  479.             incremented or decremented so the operation can be repeated.
  480.  
  481.  
  482.  
  483. IBM PC Assembly Language Tutorial                                         8
  484.  
  485.  
  486.         They include
  487.  
  488.         1)  CMPSB/CMPSW -- compare byte or word
  489.  
  490.         2)  LODSB/LODSW -- load byte or word into AL or AX
  491.  
  492.         3)  STOSB/STOSW -- store byte or word from AL or AX
  493.  
  494.         4)  MOVSB/MOVSW -- move byte or word
  495.  
  496.         5)  SCASB/SCASW -- compare byte or word with contents of AL or AX
  497.  
  498.         6)  REP/REPE/REPNE -- a prefix which can be combined with any of
  499.             the above instructions to make them execute repeatedly across a
  500.             string of data whose length is held in CX.
  501.  
  502.     f.  Flag instructions: CLI, STI, CLD, STD, CLC, STC.  These can set or
  503.         clear the interrupt (enabled) direction (for string operations) or
  504.         carry flags.
  505.  
  506.     The addressing summary and the instruction summary given above masks a
  507.     lot of annoying little exceptions.  For example, you can't POP CS, and
  508.     although the R <-- M form of LES is legal, the M <-- R form isn't etc.
  509.     etc.  My advice is
  510.  
  511.     a.  Go for the general rules
  512.  
  513.     b.  Don't try to memorize the exceptions
  514.  
  515.     c.  Rely on common sense and the assembler to teach you about
  516.         exceptions over time.  A lot of the exceptions cover things you
  517.         wouldn't want to do anyway.
  518.  
  519. 5.  A few instructions are rich enough and useful enough to warrent careful
  520.     study.  Here are a few final study guidelines:
  521.  
  522.     a.  It is well worth the time learning to use the string instruction
  523.         set effectively.  Among the most useful are
  524.  
  525.                 REP MOVSB           ;moves a string
  526.                 REP STOSB           ;initializes memory
  527.                 REPNE SCASB         ;look up occurance of character in string
  528.                 REPE CMPSB          ;compare two strings
  529.  
  530.     b.  Similarly, if you have never written for a stack machine before,
  531.         you will need to exercise PUSH and POP and get very comfortable
  532.         with them because they are going to be good friends.  If you are
  533.         used to the 370, with lots of general purpose registers, you may
  534.         find yourself feeling cramped at first, with many fewer registers
  535.         and many instructions having register restrictions.  But, you have
  536.         a hidden ally:  you need a register and you don't want to throw
  537.         away what's in it?  Just PUSH it, and when you are done, POP it
  538.         back.  This can lead to abuse.  Never have more than two
  539.         "expedient" PUSHes in effect and never leave something PUSHed
  540.         across a major header comment or for more than 15 instructions or
  541.  
  542.  
  543. IBM PC Assembly Language Tutorial                                         9
  544.  
  545.  
  546.         so.  An exception is the saving and restoring of registers at
  547.         entrance to and exit from a subroutine; here, if the subroutine is
  548.         long, you should probably PUSH everything which the caller may need
  549.         saved, whether you will use the register or not, and POP it in
  550.         reverse order at the end.
  551.  
  552.         Be aware that CALL and INT push return address information on the
  553.         stack and RET and IRET pop it off.  It is a good idea to become
  554.         familiar with the structure of the stack.
  555.  
  556.     c.  In practice, to invoke system services you will use the INT
  557.         instruction.  It is quite possible to use this instruction effec-
  558.         tively in a cookbook fashion without knowing precisely how it
  559.         works.
  560.  
  561.     d.  The transfer of control instructions (CALL, RET, JMP) deserve care-
  562.         ful study to avoid confusion.  You will learn that these can be
  563.         classified as follows:
  564.  
  565.         1)  all three have the capability of being either NEAR (CS register
  566.             unchanged) or FAR (CS register changed)
  567.  
  568.         2)  JMPs and CALLs can be DIRECT (target is assembled into instruc-
  569.             tion) or INDIRECT (target fetched from memory or register)
  570.  
  571.         3)  if NEAR and DIRECT, a JMP can be SHORT (less than 128 bytes
  572.             away) or LONG
  573.  
  574.         In general, the third issue is not worth worrying about.  On a for-
  575.         ward jump which is clearly VERY short, you can tell the assembler
  576.         it is short and save one byte of code:
  577.  
  578.                    JMP SHORT  CLOSEBY
  579.  
  580.         On a backward jump, the assembler can figure it out for you.  On a
  581.         forward jump of dubious length, let the assembler default to a LONG
  582.         form; at worst you waste one byte.
  583.  
  584.         Also leave the assembler to worry about how the target address is
  585.         to be represented, in absolute form or relative form.
  586.  
  587.     e.  The conditional jump set is rather confusing when studied apart
  588.         from the assembler, but you do need to get a feeling for it.  The
  589.         interactions of the sign, carry, and overflow flags can get your
  590.         mind stuttering pretty fast if you worry about it too much.  What
  591.         is boils down to, though, is
  592.  
  593.                 JZ        means what it says
  594.                 JNZ       means what it says
  595.                 JG reater this means "if the SIGNED difference is positive"
  596.                 JA bove   this means "if the UNSIGNED difference is positive"
  597.                 JL ess    this means "if the SIGNED difference is negative"
  598.                 JB elow   this means "if the UNSIGNED difference is negative"
  599.                 JC arry   assembles the same as JB; it's an aesthetic choice
  600.  
  601.  
  602.  
  603. IBM PC Assembly Language Tutorial                                        10
  604.  
  605.  
  606.         You should understand that all conditional jumps are inherently
  607.         DIRECT, NEAR, and "short"; the "short" part means that they can't
  608.         go more than 128 bytes in either direction.  Again, this is some-
  609.         thing you could easily imagine to be more of a problem than it is.
  610.         I follow this simple approach:
  611.  
  612.         1)  When taking an abnormal exit from a block of code, I always use
  613.             an unconditional jump.  Who knows how far you are going to end
  614.             up jumping by the time the program is finished.  For example, I
  615.             wouldn't code this:
  616.  
  617.                    TEST     AL,IDIBIT       ;Is the idiot bit on?
  618.                    JNZ      OYVEY           ;Yes.  Go to general cleanup
  619.  
  620.             Rather, I would probably code this:
  621.  
  622.                    TEST     AL,IDIBIT       ;Is the idiot bit on?
  623.                    JZ       NOIDIOCY        ;No.  I am saved.
  624.                    JMP      OYVEY           ;Yes.  What can we say...
  625.               NOIDIOCY:
  626.  
  627.             The latter, of course, is a jump around a jump.  Some would say
  628.             it is evil, but I submit it is hard to avoid in this language.
  629.  
  630.         2)  Otherwise, within a block of code, I use conditional jumps
  631.             freely.  If the block eventually grows so long that the assem-
  632.             bler starts complaining that my conditional jumps are too long
  633.             I
  634.  
  635.             a)  consider reorganizing the block but
  636.  
  637.             b)  also consider changing some conditional jumps to their
  638.                 opposite and use the "jump around a jump" approach as shown
  639.                 above.
  640.  
  641.     Enough about specific instructions!
  642.  
  643. 6.  Finally, in order to use the assembler effectively, you need to know
  644.     the default rules for which segment registers are used to complete
  645.     addresses in which situations.
  646.  
  647.     a.  CS is used to complete an address which is the target of a NEAR
  648.         DIRECT jump.  On an NEAR INDIRECT jump, DS is used to fetch the
  649.         address from memory but then CS is used to complete the address
  650.         thus fetched.  On FAR jumps, of course, CS is itself altered.  The
  651.         instruction counter is always implicitly pointing in the code seg-
  652.         ment.
  653.  
  654.     b.  SS is used to complete an address if BP is used in its formation.
  655.         Otherwise, DS is always used to complete a data address.
  656.  
  657.     c.  On the string instructions, the target is always formed from ES and
  658.         DI.  The source is normally formed from DS and SI.  If there is a
  659.         segment prefix, it overrides the source not the target.
  660.  
  661.  
  662.  
  663. IBM PC Assembly Language Tutorial                                        11
  664.  
  665.  
  666. Learning about DOS
  667. __________________
  668.  
  669.  
  670. I think the best way to learn about DOS internals is to read the technical
  671. appendices in the manual.  These are not as complete as we might wish, but
  672. they really aren't bad; I certainly have learned a lot from them.  What you
  673. don't learn from them you might eventually learn via judicious disassembly
  674. of parts of DOS, but that shouldn't really be necessary.
  675.  
  676. From reading the technical appendices, you learn that interrupts 20H
  677. through 27H are used to communicate with DOS.  Mostly, you will use inter-
  678. rupt 21H, the DOS function manager.
  679.  
  680. The function manager implements a great many services.  You request the
  681. individual services by means of a function code in the AH register.  For
  682. example, by putting a nine in the AH register and issuing interrupt 21H you
  683. tell DOS to print a message on the console screen.
  684.  
  685. Usually, but by no means always, the DX register is used to pass data for
  686. the service being requested.  For example, on the print message service
  687. just mentioned, you would put the 16 bit address of the message in the DX
  688. register.  The DS register is also implicitly part of this argument, in
  689. keeping with the universal segmentation rules.
  690.  
  691. In understanding DOS functions, it is useful to understand some history and
  692. also some of the philosophy of MS-DOS with regard to portability.  General-
  693. ly, you will find, once you read the technical information on DOS and also
  694. the IBM technical reference, you will know more than one way to do almost
  695. anything.  Which is best?  For example, to do asynch adapter I/O, you can
  696. use the DOS calls (pretty incomplete), you can use BIOS, or you can go
  697. directly to the hardware.  The same thing is true for most of the other
  698. primitive I/O (keyboard or screen) although DOS is more likely to give you
  699. added value in these areas.  When it comes to file I/O, DOS itself offers
  700. more than one interface.  For example, there are four calls which read data
  701. from a file.
  702.  
  703. The way to decide rationally among these alternatives is by understanding
  704. the tradeoffs of functionality versus portability.  Three kinds of porta-
  705. bility need to be considered:  machine portability, operating system porta-
  706. bility (for example, the ability to assemble and run code under CP/M 86)
  707. and DOS version portability (the ability for a program to run under older
  708. versions of DOS>.
  709.  
  710. Most of the functions originally offered in DOS 1.0 were direct descendents
  711. of CP/M functions; there is even a compatibility interface so that programs
  712. which have been translated instruction for instruction from 8080 assembler
  713. to 8086 assembler might have a reasonable chance of running if they use
  714. only the core CP/M function set.  Among the most generally useful in this
  715. original compatibility set are
  716.  
  717.  
  718.  
  719.  
  720.  
  721.  
  722.  
  723.  
  724. IBM PC Assembly Language Tutorial                                        12
  725.  
  726.  
  727.   09   --  print a full message on the screen
  728.   0A   --  get a console input line with full DOS editing
  729.   0F   --  open a file
  730.   10   --  close a file (really needed only when writing)
  731.   11   --  find first file matching a pattern
  732.   12   --  find next file matching a pattern
  733.   13   --  erase a file
  734.   16   --  create a file
  735.   17   --  rename a file
  736.   1A   --  set disk transfer address
  737.  
  738. The next set provide no function above what you can get with BIOS calls or
  739. more specialized DOS calls.  However, they are preferable to BIOS calls
  740. when portability is an issue.
  741.  
  742.   00   --  terminate execution
  743.   01   --  read keyboard character
  744.   02   --  write screen character
  745.   03   --  read COM port character
  746.   04   --  write COM port character
  747.   05   --  print a character
  748.   06   --  read keyboard or write screen with no editing
  749.  
  750. The standard file I/O calls are inferior to the specialized DOS calls but
  751. have the advantage of making the program easier to port to CP/M style sys-
  752. tems.  Thus they are worth mentioning:
  753.  
  754.   14   --  sequential read from file
  755.   15   --  sequential write to file
  756.   21   --  random read from file
  757.   22   --  random write to file
  758.   23   --  determine file size
  759.   24   --  set random record
  760.  
  761. In addition to the CP/M compatible services, DOS also offers some special-
  762. ized services which have been available in all releases of DOS.  These
  763. include
  764.  
  765.   27   --  multi-record random read.
  766.   28   --  multi-record random write.
  767.   29   --  parse filename
  768.   2A-2D -- get and set date and time
  769.  
  770. All of the calls mentioned above which have anything to do with files make
  771. use of a data area called the "FILE CONTROL BLOCK" (FCB).  The FCB is any-
  772. where from 33 to 37 bytes long depending on how it is used.  You are
  773. responsible for creating an FCB and filling in the first 12 bytes, which
  774. contain a drive code, a file name, and an extension.
  775.  
  776. When you open the FCB, the system fills in the next 20 bytes, which
  777. includes a logical record length.  The initial lrecl is always 128 bytes,
  778. to achieve CP/M compatibility.  The system also provides other useful
  779. information such as the file size.
  780.  
  781.  
  782.  
  783.  
  784. IBM PC Assembly Language Tutorial                                        13
  785.  
  786.  
  787. After you have opened the FCB, you can change the logical record length.
  788. If you do this, your program is no longer CP/M compatible, but that doesn't
  789. make it a bad thing to do.  DOS documentation suggests you use a logical
  790. record length of one for maximum flexibility.  This is usually a good
  791. recommendation.
  792.  
  793. To perform actual I/O to a file, you eventually need to fill in byte 33 or
  794. possibly bytes 34-37 of the FCB.  Here you supply information about the
  795. record you are interested in reading or writing.  For the most part, this
  796. part of the interface is compatible with CP/M.
  797.  
  798. In general, you do not need to (and should not) modify other parts of the
  799. FCB.
  800.  
  801. The FCB is pretty well described in appendix E of the DOS manual.
  802.  
  803. Beginning with DOS 2.0, there is a whole new system of calls for managing
  804. files which don't require that you build an FCB at all.  These calls are
  805. quite incompatible with CP/M and also mean that your program cannot run
  806. under older releases of DOS.  However, these calls are very nice and easy
  807. to use.  They have these characteristics
  808.  
  809. 1.  To open, create, delete, or rename a file, you need only a character
  810.     string representing its name.
  811.  
  812. 2.  The open and create calls return a 16 bit value which is simply placed
  813.     in the BX register on subsequent calls to refer to the file.
  814.  
  815. 3.  There is not a separate call required to specify the data buffer.
  816.  
  817. 4.  Any number of bytes can be transfered on a single call; no data area
  818.     must be manipulated to do this.
  819.  
  820. The "new" DOS calls also include comprehensive functions to manipulate the
  821. new chained directory structure and to allocate and free memory.
  822.  
  823.  
  824.  
  825. Learning the assembler
  826. ______________________
  827.  
  828.  
  829. It is my feeling that many people can teach themselves to use the assembler
  830. by reading the MACRO Assembler manual if
  831.  
  832. 1.  You have read and understood a book like Morse and thus have a feeling
  833.     for the instruction set
  834.  
  835. 2.  You know something about DOS services and so can communicate with the
  836.     keyboard and screen and do something marginally useful with files.  In
  837.     the absence of this kind of knowledge, you can't write meaningful prac-
  838.     tice programs and so will not progress.
  839.  
  840. 3.  You have access to some good examples (the ones supplied with the
  841.     assembler are not good, in my opinion.  I will try to supply you with
  842.     some more relevant ones.
  843.  
  844.  
  845. IBM PC Assembly Language Tutorial                                        14
  846.  
  847.  
  848. 4.  You ignore the things which are most confusing and least useful.  Some
  849.     of the most confusing aspects of the assembler include the facilities
  850.     combining segments.  But, you can avoid using all but the simplest of
  851.     these facilities in many cases, even while writing quite substantial
  852.     applications.
  853.  
  854. 5.  The easiest kind of assembler program to write is a COM program.  They
  855.     might seem harder, at first, then EXE programs because there is an
  856.     extra step involved in creating the executable file, but COM programs
  857.     are structurally very much simpler.
  858.  
  859. At this point, it is necessary to talk about COM programs and EXE programs.
  860. As you probably know, DOS supports two kinds of executable files.  EXE pro-
  861. grams are much more general, can contain many segments, and are generally
  862. built by compilers and sometimes by the assembler.  If you follow the lead
  863. given by the samples distributed with the assembler, you will end up with
  864. EXE programs.  A COM program, in contrast, always contains just one
  865. segment, and receives control with all four segment registers containing
  866. the same value.  A COM program, thus, executes in a simplified environment,
  867. a 64K address space.  You can go outside this address space simply by tem-
  868. porarily changing one segment register, but you don't have to, and that is
  869. the thing which makes COM programs nice and simple.  Let's look at a very
  870. simple one.
  871.  
  872. The classic text on writing programs for the C language says that the first
  873. thing you should write is a program which says
  874.  
  875.       HELLO, WORLD.
  876.  
  877. when invoked.  What's sauce for C is sauce for assembler, so let's start
  878. with a HELLO program of our own.  My first presentation of this will be
  879. bare bones, not stylistically complete, but just an illustration of what an
  880. assembler program absolutely has to have:
  881.  
  882. HELLO  SEGMENT                  ;Set up HELLO code and data section
  883.        ASSUME CS:HELLO,DS:HELLO ;Tell assembler about conditions at entry
  884.        ORG  100H                ;A .COM program begins with 100H byte prefix
  885. MAIN:  JMP  BEGIN               ;Control must start here
  886. MSG    DB   'Hello, world.$'    ;But it is generally useful to put data first
  887. BEGIN: MOV  DX,OFFSET MSG       ;Let DX --> message.
  888.        MOV  AH,9                ;Set DOS function code for printing a message
  889.        INT  21H                 ;Invoke DOS
  890.        RET                      ;Return to system
  891. HELLO  ENDS                     ;End of code and data section
  892.         END  MAIN                 ;Terminate assembler and specify entry point
  893.  
  894.  
  895. First, let's attend to some obvious points.  The macro assembler uses the
  896. general form
  897.  
  898.    name    opcode    operands
  899.  
  900. Unlike the 370 assembler, though, comments are NOT set off from operands by
  901. blanks.  The syntax uses blanks as delimiters within the operand field (see
  902. line 6 of the example) and so all comments must be set off by semi-colons.
  903.  
  904.  
  905. IBM PC Assembly Language Tutorial                                        15
  906.  
  907.  
  908. Line comments are frequently set off with a semi-colon in column 1.  I use
  909. this approach for block comments too, although there is a COMMENT statement
  910. which can be used to introduce a block comment.
  911.  
  912. Being an old 370 type, I like to see assembler code in upper case, although
  913. my comments are mixed case.  Actually, the assembler is quite happy with
  914. mixed case anywhere.
  915.  
  916. As with any assembler, the core of the opcode set consists of opcodes which
  917. generate machine instructions but there are also opcodes which generate
  918. data and ones which function as instructions to the assembler itself, some-
  919. times called pseudo-ops.  In the example, there are five lines which gener-
  920. ate machine code (JMP, MOV, MOV, INT, RET), one line which generates data
  921. (DB) and five pseudo-ops (SEGMENT, ASSUME, ORG, ENDS, and END).
  922.  
  923. We will discuss all of them.
  924.  
  925. Now, about labels.  You will see that some labels in the example end in a
  926. colon and some don't.  This is just a bit confusing at first, but no real
  927. mystery.  If a label is attached to a piece of code (as opposed to data),
  928. then the assembler needs to know what to do when you JMP to or CALL that
  929. label.  By convention, if the label ends in a colon, the assembler will use
  930. the NEAR form of JMP or CALL.  If the label does not end in a colon, it
  931. will use the FAR form.  In practice, you will always use the colon on any
  932. label you are jumping to inside your program because such jumps are always
  933. NEAR; there is no reason to use a FAR jump within a single code section.  I
  934. mention this, though, because leaving off the colon isn't usually trapped
  935. as a syntax error, it will generally cause something more abstruse to go
  936. wrong.
  937.  
  938. On the other hand, a label attached to a piece of data or a pseudo-op never
  939. ends in a colon.
  940.  
  941. Machine instructions will generally take zero, one or two operands.  Where
  942. there are two operands, the one which receives the result goes on the left
  943. as in 370 assembler.
  944.  
  945. I tried to explain this before, now maybe it will be even clearer:  there
  946. are many more 8086 machine opcodes then there are assembler opcodes to rep-
  947. resent them.  For example, there are five kinds of JMP, four kinds of CALL,
  948. two kinds of RET, and at least five kinds of MOV depending on how you count
  949. them.  The macro assembler makes a lot of decisions for you based on the
  950. form taken by the operands or on attributes assigned to symbols elsewhere
  951. in your program.  In the example above, the assembler will generate the
  952. NEAR DIRECT form of JMP because the target label BEGIN labels a piece of
  953. code instead of a piece of data (this makes the JMP DIRECT) and ends in a
  954. colon (this makes the JMP NEAR).  The assembler will generate the immediate
  955. forms of MOV because the form OFFSET MSG refers to immediate data and
  956. because 9 is a constant.  The assembler will generate the NEAR form of RET
  957. because that is the default and you have not told it otherwise.
  958.  
  959. The DB (define byte) pseudo-op is an easy one:  it is used to put one or
  960. more bytes of data into storage.  There is also a DW (define word)
  961. pseudo-op and a  DD (define doubleword) pseudo-op;  in the PC MACRO assem-
  962. bler, the fact that a label refers to a byte of storage, a word of storage,
  963.  
  964.  
  965. IBM PC Assembly Language Tutorial                                        16
  966.  
  967.  
  968. or a doubleword of storage can be very significant in ways which we will
  969. see presently.
  970.  
  971. About that OFFSET operator, I guess this is the best way to make the point
  972. about how the assembler decides what instruction to assemble:  an analogy
  973. with 370 assembler:
  974.  
  975.   PLACE    DC   ......
  976.            ...
  977.            LA   R1,PLACE
  978.            L    R1,PLACE
  979.  
  980. In 370 assembler, the first instruction puts the address of label PLACE in
  981. register 1, the second instruction puts the contents of storage at label
  982. PLACE in register 1.  Notice that two different opcodes are used.  In the
  983. PC assembler, the analogous instructions would be
  984.  
  985.   PLACE    DW   ......
  986.            ...
  987.            MOV  DX,OFFSET PLACE
  988.            MOV  DX,PLACE
  989.  
  990. If PLACE is the label of a word of storage, then the second instruction
  991. will be understood as a desire to fetch that data into DX.  If X is a
  992. label, then "OFFSET X" means "the ordinary number which represents X's off-
  993. set from the start of the segment."  And, if the assembler sees an ordinary
  994. number, as opposed to a label, it uses the instruction which is equivalent
  995. to LA.
  996.  
  997. If PLACE were the label of a DB pseudo-op, instead of a DW, then
  998.  
  999.            MOV  DX,PLACE
  1000.  
  1001. would be illegal.  The assembler worries about length attributes of its
  1002. operands.
  1003.  
  1004. Next, numbers and constants in general.  The assembler's default radix is
  1005. decimal.  You can change this, but I don't recommend it.  If you want to
  1006. represent numbers in other forms of notation such as hex or bit, you gener-
  1007. ally use a trailing letter.  For example,
  1008.  
  1009.            21H
  1010.   is hexidecimal 21,
  1011.            00010000B
  1012.   is the eight bit binary number pictured.
  1013.  
  1014. The next elements we should point to are the SEGMENT...ENDS pair and the
  1015. END instruction.  Every assembler program has to have these elements.
  1016.  
  1017. SEGMENT tells the assembler you are starting a section of contiguous mate-
  1018. rial (code and/or data).  The symmetrically named ENDS statement tells the
  1019. assembler you are finished with a section of contiguous material.  I wish
  1020. they didn't use the word SEGMENT in this context.  To me, a "segment" is a
  1021. hardware construct:  it is the 64K of real storage which becomes address-
  1022. able by virtue of having a particular value in a segment register.  Now, it
  1023.  
  1024.  
  1025. IBM PC Assembly Language Tutorial                                        17
  1026.  
  1027.  
  1028. is true that the "segments" you make with the assembler often correspond to
  1029. real hardware "segments" at execution time.  But, if you look at things
  1030. like the GROUP and CLASS options supported by the linker, you will discover
  1031. that this correspondence is by no means exact.  So, at risk of maybe con-
  1032. fusing you even more, I am going to use the more informal term "section" to
  1033. refer to the area set off by means of the SEGMENT and ENDS instructions.
  1034.  
  1035. The sections delimited by SEGMENT...ENDS pairs are really a lot like CSECTs
  1036. and DSECTs in the 370 world.
  1037.  
  1038. I strongly recommend that you be selective in your study of the SEGMENT
  1039. pseudo-op as described in the manual.  Let me just touch on it here.
  1040.  
  1041.   name     SEGMENT
  1042.   name     SEGMENT  PUBLIC
  1043.   name     SEGMENT  AT  nnn
  1044.  
  1045. Basically, you can get away with just the three forms given above.  The
  1046. first form is what you use when you are writing a single section of assem-
  1047. bler code which will not be combined with other pieces of code at link
  1048. time.   The second form says that this assembly only contains part of the
  1049. section;  other parts might be assembled separately and combined later by
  1050. the linker.
  1051.  
  1052. I have found that one can construct reasonably large modular applications
  1053. in assembler by simply making every assembly use the same segment name and
  1054. declaring the name to be PUBLIC each time.  If you read the assembler and
  1055. linker documentation, you will also be bombarded by information about more
  1056. complex options such as the GROUP statement and the use of other "combine
  1057. types" and "classes."  I don't recommend getting into any of that.  I will
  1058. talk more about the linker and modular construction of programs a little
  1059. later.  The assembler manual also implies that a STACK segment is required.
  1060. This is not really true.  There are numerous ways to assure that you have a
  1061. valid stack at execution time.
  1062.  
  1063. Of course, if you plan to write applications in assembler which are more
  1064. than 64K in size, you will need more than what I have told you; but who is
  1065. really going to do that?  Any application that large is likely to be coded
  1066. in a higher level language.
  1067.  
  1068. The third form of the SEGMENT statement makes the delineated section into
  1069. something like a "DSECT;" that is, it doesn't generate any code, it just
  1070. describes what is present somewhere already in the computer's memory.
  1071. Sometimes the AT value you give is meaningful.  For example, the BIOS work
  1072. area is located at location 40 hex.  So, you might see
  1073.  
  1074.   BIOSAREA  SEGMENT AT 40H      ;Map BIOS work area
  1075.             ORG  BIOSAREA+10H
  1076.   EQUIP     DB   ?              ;Location of equipment flags, first byte
  1077.   BIOSAREA  ENDS
  1078.  
  1079. in a program which was interested in mucking around in the BIOS work area.
  1080.  
  1081. At other times, the AT value you give may be arbitrary, as when you are
  1082. mapping a repeated control block:
  1083.  
  1084.  
  1085. IBM PC Assembly Language Tutorial                                        18
  1086.  
  1087.  
  1088.   PROGPREF SEGMENT   AT 0      ;Really a DSECT mapping the program prefix
  1089.            ORG   PROGPREF+6
  1090.   MEMSIZE  DW   ?              ;Size of available memory
  1091.   PROGPREF ENDS
  1092.  
  1093. Really, no matter whether the AT value represents truth or fiction, it is
  1094. your responsibility, not the assembler's, to get set up a segment register
  1095. so that you can really reach the storage in question.   So, you can't say
  1096.  
  1097.          MOV  AL,EQUIP
  1098.  
  1099. unless you first say something like
  1100.  
  1101.          MOV  AX,BIOSAREA   ;BIOSAREA becomes a symbol with value 40H
  1102.          MOV  ES,AX
  1103.          ASSUME ES:BIOSAREA
  1104.  
  1105. Enough about SEGMENT.  The END statement is simple.  It goes at the end of
  1106. every assembly.  When you are assembling a subroutine, you just say
  1107.  
  1108.          END
  1109.  
  1110. but when you are assembling the main routine of a program you say
  1111.  
  1112.         END label
  1113.  
  1114. where 'label' is the place where execution is to begin.
  1115.  
  1116. Another pseudo-op illustrated in the program is ASSUME.  ASSUME is like the
  1117. USING statement in 370 assembler.  However, ASSUME can ONLY refer to seg-
  1118. ment registers.  The assembler uses ASSUME information to decide whether to
  1119. assemble segment override prefixes and to check that the data you are try-
  1120. ing to access is really accessible.  In this case, we can reassure the
  1121. assembler that both the CS and DS registers will address the section called
  1122. HELLO at execution time.  Actually, the SS and ES registers will too, but
  1123. the assembler never needs to make use of this information.
  1124.  
  1125. I guess I have explained everything in the program except that ORG
  1126. pseudo-op.  ORG means the same thing as it does in many assembly languages.
  1127. It tells the assembler to move its location counter to some particular
  1128. address.  In this case, we have asked the assembler to start assembling
  1129. code hex 100 bytes from the start of the section called HELLO instead of at
  1130. the very beginning.  This simply reflects the way COM programs are loaded.
  1131. When a COM program is loaded by the system, the system sets up all four
  1132. segment registers to address the same 64K of storage.  The first 100 hex
  1133. bytes of that storage contains what is called the program prefix; this area
  1134. is described in appendix E of the DOS manual.  Your COM program physically
  1135. begins after this.  Execution begins with the first physical byte of your
  1136. program; that is why the JMP instruction is there.
  1137.  
  1138. Wait a minute, you say, why the JMP instruction at all?  Why not put the
  1139. data at the end?  Well, in a simple program like this I probably could have
  1140. gotten away with that.  However, I have the habit of putting data first and
  1141. would encourage you to do the same because of the way the assembler has of
  1142. assembling different instructions depending on the nature of the operand.
  1143.  
  1144.  
  1145. IBM PC Assembly Language Tutorial                                        19
  1146.  
  1147.  
  1148. Unfortunately, sometimes the different choices of instruction which can
  1149. assemble from a single opcode have different lengths.  If the assembler has
  1150. already seen the data when it gets to the instructions it has a good chance
  1151. of reserving the right number of bytes on the first pass.  If the data is
  1152. at the end, the assembler may not have enough information on the first pass
  1153. to reserve the right number of bytes for the instruction.  Sometimes the
  1154. assembler will complain about this, something like "Forward reference is
  1155. illegal" but at other times, it will make some default assumption.  On the
  1156. second pass, if the assumption turned out to be wrong, it will report what
  1157. is called a "Phase error," a very nasty error to track down.  So get in the
  1158. habit of putting data and equated symbols ahead of code.
  1159.  
  1160. OK.  Maybe you understand the program now.  Let's walk through the steps
  1161. involved in making it into a real COM file.
  1162.  
  1163. 1.  The file should be created with the name HELLO.ASM (actually the name
  1164.     is arbitrary but the extension .ASM is conventional and useful)
  1165.  
  1166. 2.
  1167.  
  1168.       ASM   HELLO,,;
  1169.  
  1170.     (this is just one example of invoking the assembler; it uses the small
  1171.     assembler ASM, it produces an object file and a listing file with the
  1172.     same name as the source file.  I am not going exhaustively into how to
  1173.     invoke the assembler, which the manual goes into pretty well.  I guess
  1174.     this is the first time I mentioned that there are really two
  1175.     assemblers; the small assembler ASM will run in a 64K machine and
  1176.     doesn't support macros.  I used to use it all the time; now that I have
  1177.     a bigger machine and a lot of macro libraries I use the full function
  1178.     assembler MASM.  You get both when you buy the package).
  1179.  
  1180. 3.  If you issue DIR at this point, you will discover that you have
  1181.     acquired HELLO.OBJ (the object code resulting from the assembly) and
  1182.     HELLO.LST (a listing file).  I guess I can digress for a second here
  1183.     concerning the listing file.  It contains TAB characters.  I have found
  1184.     there are two good ways to get it printed and one bad way.  The bad way
  1185.     is to use LPT1: as the direct target of the listing file or to try
  1186.     copying the LST file to LPT1 without first setting the tabs on the
  1187.     printer.  The two good ways are to either
  1188.  
  1189.     a.  direct it to the console and activate the printer with CTRL-PRTSC.
  1190.         In this case, DOS will expand the tabs for you.
  1191.  
  1192.     b.  direct to LPT1: but first send the right escape sequence to LPT1 to
  1193.         set the tabs every eight columns.  I have found that on some early
  1194.         serial numbers of the IBM PC printer, tabs don't work quite right,
  1195.         which forces you to the first option.
  1196.  
  1197. 4.
  1198.  
  1199.           LINK  HELLO;
  1200.  
  1201.     (again, there are lots of linker options but this is the simplest.  It
  1202.     takes HELLO.OBJ and makes HELLO.EXE).  HELLO.EXE?  I thought we were
  1203.  
  1204.  
  1205. IBM PC Assembly Language Tutorial                                        20
  1206.  
  1207.  
  1208.     making a COM program, not an EXE program.  Right.  HELLO.EXE isn't
  1209.     really executable; its just that the linker doesn't know about COM pro-
  1210.     grams.  That requires another utility.  You don't have this utility if
  1211.     you are using DOS 1.0; you have it if you are using DOS 1.1 or DOS 2.0.
  1212.     Oh, by the way, the linker will warn you that you have no stack
  1213.     segment.  Don't worry about it.
  1214.  
  1215. 5.
  1216.  
  1217.           EXE2BIN  HELLO HELLO.COM
  1218.  
  1219.     This is the final step.  It produces the actual program you will exe-
  1220.     cute.  Note that you have to spell out HELLO.COM; for a nominally
  1221.     rational but actually perverse reason, EXE2BIN uses the default exten-
  1222.     sion BIN instead of COM for its output file.  At this point, you might
  1223.     want to erase HELLO.EXE; it looks a lot more useful than it is.
  1224.     Chances are you won't need to recreate HELLO.COM unless you change the
  1225.     source and then you are going to have to redo the whole thing.
  1226.  
  1227. 6.
  1228.  
  1229.           HELLO
  1230.  
  1231.     You type hello, that invokes the program, it says
  1232.  
  1233.           HELLO YOURSELF!!!
  1234.  
  1235.     (oops, what did I do wrong....?)
  1236.  
  1237.  
  1238.  
  1239. What about subroutines?
  1240. _______________________
  1241.  
  1242.  
  1243. I started with a simple COM program because I actually think they are easi-
  1244. er to create than subroutines to be called from high level languages, but
  1245. maybe its really the latter you are interested in.  Here, I think you
  1246. should get comfortable with the assembler FIRST with little exercises like
  1247. the one above and also another one which I will finish up with.
  1248.  
  1249. Next you are ready to look at the interface information for your particular
  1250. language.  You usually find this in some sort of an appendix.  For example,
  1251. the BASIC manual has Appendix C on Machine Language Subroutines.  The
  1252. PASCAL manual buries the information a little more deeply:  the interface
  1253. to a separately compiled routine can be found in the Chapter on Procedures
  1254. and Functions, in a subsection called Internal Calling Conventions.
  1255.  
  1256. Each language is slightly different, but here are what I think are some
  1257. common issues in subroutine construction.
  1258.  
  1259. 1.  NEAR versus FAR?  Most of the time, your language will probably call
  1260.     your assembler routine as a FAR routine.  In this case, you need to
  1261.     make sure the assembler will generate the right kind of return.  You do
  1262.     this with a PROC...ENDP statement pair.  The PROC statement is probably
  1263.  
  1264.  
  1265.  
  1266. IBM PC Assembly Language Tutorial                                        21
  1267.  
  1268.  
  1269.     a good idea for a NEAR routine too even though it is not strictly
  1270.     required:
  1271.  
  1272.               FAR linkage:        |            NEAR linkage:
  1273.                                   |
  1274.     ARBITRARY SEGMENT             |  SPECIFIC  SEGMENT  PUBLIC
  1275.               PUBLIC THENAME      |            PUBLIC THENAME
  1276.               ASSUME CS:ARBITRARY |            ASSUME CS:SPECIFIC,DS:SPECIFIC
  1277.     THENAME   PROC FAR            |            ASSUME ES:SPECIFIC,SS:SPECIFIC
  1278.               ..... code and data |  THENAME   PROC NEAR
  1279.     THENAME   ENDP                |            ..... code and data ....
  1280.     ARBITRARY ENDS                |  THENAME   ENDP
  1281.               END                 |  SPECIFIC  ENDS
  1282.                                   |            END
  1283.  
  1284.  
  1285.     With FAR linkage, it doesn't really matter what you call the segment.
  1286.     you must declare the name by which you will be called in a PUBLIC pseu-
  1287.     do-op and also show that it is a FAR procedure.  Only CS will be ini-
  1288.     tialized to your segment when you are called.  Generally, the other
  1289.     segment registers will continue to point to the caller's segments.
  1290.  
  1291.     With NEAR linkage, you are executing in the same segment as the caller.
  1292.     Therefore, you must give the segment a specific name as instructed by
  1293.     the language manual.  However, you may be able to count on all segment
  1294.     registers pointing to your own segment (sometimes the situation can be
  1295.     more complicated but I cannot really go into all of the details).  You
  1296.     should be aware that the code you write will not be the only thing in
  1297.     the segment and will be physically relocated within the segment by the
  1298.     linker.  However, all OFFSET references will be relocated and will be
  1299.     correct at execution time.
  1300.  
  1301. 2.  Parameters passed on the stack.  Usually, high level languages pass
  1302.     parameters to subroutines by pushing words onto the stack prior to
  1303.     calling you.  What may differ from language to language is the nature
  1304.     of what is pushed (OFFSET only or OFFSET and SEGMENT) and the order in
  1305.     which it is pushed (left to right, right to left within the CALL state-
  1306.     ment).  However, you will need to study the examples to figure out how
  1307.     to retrieve the parameters from the stack.  A useful fact to exploit is
  1308.     the fact that a reference involving the BP register defaults to a ref-
  1309.     erence to the stack segment.  So, the following strategy can work:
  1310.  
  1311.       ARGS     STRUC
  1312.                DW   3 DUP(?)  ;Saved BP and return address
  1313.       ARG3     DW   ?
  1314.       ARG2     DW   ?
  1315.       ARG1     DW   ?
  1316.       ARGS     ENDS
  1317.            ...........
  1318.                PUSH BP                 ;save BP register
  1319.                MOV  BP,SP              ;Use BP to address stack
  1320.                MOV   ...,[BP].ARG2     ;retrieve second argument
  1321.                (etc.)
  1322.  
  1323.  
  1324.  
  1325.  
  1326. IBM PC Assembly Language Tutorial                                        22
  1327.  
  1328.  
  1329.     This example uses something called a structure, which is only available
  1330.     in the large assembler; furthermore, it uses it without allocating it,
  1331.     which is not a well-documented option.  However, I find the above
  1332.     approach generally pleasing.  The STRUC is like a DSECT in that it
  1333.     establishes labels as being offset a certain distance from an arbitrary
  1334.     point; these labels are then used in the body of code by beginning them
  1335.     with a period; the construction ".ARG2" means, basically, " +
  1336.     (ARG2-ARGS)."
  1337.  
  1338.     What you are doing here is using BP to address the stack, accounting
  1339.     for the word where you saved the caller's BP and also for the two words
  1340.     which were pushed by the CALL instruction.
  1341.  
  1342. 3.  How big is the stack?  BASIC only gives you an eight word stack to play
  1343.     with.  On the other hand, it doesn't require you to save any registers
  1344.     except the segment registers.  Other languages give you a liberal
  1345.     stack, which makes things a lot easier.  If you have to create a new
  1346.     stack segment for yourself, the easiest thing is to place the stack at
  1347.     the end of your program and:
  1348.  
  1349.          CLI                      ;suppress interrupts while changing the stack
  1350.          MOV  SSAVE,SS            ;save old SS in local storage (old SP
  1351.                                   ; already saved in BP)
  1352.          MOV  SP,CS               ;switch
  1353.          MOV  SS,SP               ;the
  1354.          MOV  SP,OFFSET STACKTOP  ;stack
  1355.          STI                      ;(maybe)
  1356.  
  1357.     Later, you can reverse these steps before returning to the caller.  At
  1358.     the end of your program, you place the stack itself:
  1359.  
  1360.              DW   128 DUP(?)          ;stack of 128 words (liberal)
  1361.     STACKTOP LABEL WORD
  1362.  
  1363.  
  1364. 4.  Make sure you save and restore those registers required by the caller.
  1365.  
  1366. 5.  Be sure to get the right kind of addressibility.  In the FAR call exam-
  1367.     ple, only CS addresses your segment.  If you are careful with your
  1368.     ASSUME statements the assembler will keep track of this fact and gener-
  1369.     ate CS prefixes when you make data references; however, you might want
  1370.     to do something like
  1371.  
  1372.                     MOV AX,CS      ;get current segment address
  1373.                     MOV DS,AX      ;To DS
  1374.                     ASSUME DS:THISSEG
  1375.  
  1376.     Be sure you keep your ASSUMEs in synch with reality.
  1377.  
  1378.  
  1379.  
  1380.  
  1381.  
  1382.  
  1383.  
  1384.  
  1385.  
  1386. IBM PC Assembly Language Tutorial                                        23
  1387.  
  1388.  
  1389. Learning about BIOS and the hardware
  1390. ____________________________________
  1391.  
  1392.  
  1393. You can't do everything with DOS calls.  You may need to learn something
  1394. about the BIOS and about the hardware itself.  In this, the Technical Ref-
  1395. erence is a very good thing to look at.
  1396.  
  1397. The first thing you look at in the Technical Reference, unless you are
  1398. really determined to master the whole ball of wax, is the BIOS listings
  1399. presented in Appendix A. Glory be:  here is the whole 8K of ROM which deals
  1400. with low level hardware support layed out with comments and everything.
  1401.  
  1402. In fact, if you are just interested in learning what BIOS can do for you,
  1403. you just need to read the header comments at the beginning of each section
  1404. of the listing.
  1405.  
  1406. BIOS services are invoked by means of the INT instruction; the BIOS occu-
  1407. pies interrupts 10H through 1FH and also interrupt 5H; actually, of these
  1408. seventeen interrupts, five are used for user exit points or data pointers,
  1409. leaving twelve actual services.
  1410.  
  1411. In most cases, a service deals with a particular hardware interface; for
  1412. example, BIOS interrupt 10H deals with the screen.  As with DOS function
  1413. calls, many BIOS services can be passed a function code in the AH register
  1414. and possible other arguments.
  1415.  
  1416. I am not going to summarize the most useful BIOS features here; you will
  1417. see some examples in the next sample program we will look at.
  1418.  
  1419. The other thing you might want to get into with the Tech reference is the
  1420. description of some hardware options, particularly the asynch adapter,
  1421. which are not well supported in the BIOS.  The writeup on the asynch adapt-
  1422. er is pretty complete.
  1423.  
  1424. Actually, the Tech reference itself is pretty complete and very nice as far
  1425. as it goes.  One thing which is missing from the Tech reference is informa-
  1426. tion on the programmable peripheral chips on the system board.  These
  1427. include
  1428.  
  1429.       the 8259 interrupt controller
  1430.       the 8253 timer
  1431.       the 8237 DMA controller and
  1432.       the 8255 peripheral interface
  1433.  
  1434. To make your library absolutely complete, you should order the INTEL data
  1435. sheets for these beasts.
  1436.  
  1437. I should say, though, that the only I ever found I needed to know about was
  1438. the interrupt controller.  If you happen to have the 8086 Family User's
  1439. Manual, the big book put out by INTEL, which is one of the things people
  1440. sometimes buy to learn about 8086 architecture, there is an appendix there
  1441. which gives an adequate description of the 8259.
  1442.  
  1443.  
  1444.  
  1445.  
  1446.  
  1447. IBM PC Assembly Language Tutorial                                        24
  1448.  
  1449.  
  1450. A final example
  1451. _______________
  1452.  
  1453.  
  1454. I leave you with a more substantial example of code which illustrates some
  1455. good elementary techniques; I won't claim its style is perfect, but I think
  1456. it is adequate.  I think this is a much more useful example than what you
  1457. will get with the assembler:
  1458.  
  1459.           PAGE 61,132
  1460.           TITLE SETSCRN -- Establish correct monitor use at boot time
  1461. ;
  1462. ;         This program is a variation on many which toggle the equipment flags
  1463. ;         to support the use of either video option (monochrome or color).
  1464. ;         The thing about this one is it prompts the user in such a way that he
  1465. ;         can select the use of the monitor he is currently looking at (or which
  1466. ;         is currently connected or turned on) without really having to know
  1467. ;         which is which.  SETSCRN is a good program to put first in an
  1468. ;         AUTOEXEC.BAT file.
  1469. ;
  1470. ;         This program is highly dependent on the hardware and BIOS of the IBMPC
  1471. ;         and is hardly portable, except to very exact clones.  For this reason,
  1472. ;         BIOS calls are used in lieu of DOS function calls where both provide
  1473. ;         equal function.
  1474. ;
  1475.  
  1476.  
  1477. OK.  That's the first page of the program.  Notice the PAGE statement,
  1478. which you can use to tell the assembler how to format the listing.  You
  1479. give it lines per page and characters per line.  I have mine setup to print
  1480. on the host lineprinter; I routinely upload my listings at 9600 baud and
  1481. print them on the host; it is faster than using the PC printer.
  1482.  
  1483. There is also a TITLE statement.  This simply provides a nice title for
  1484. each page of your listing.  Now for the second page:
  1485.  
  1486.           SUBTTL -- Provide .COM type environment and Data
  1487.           PAGE
  1488. ;
  1489. ;         First, describe the one BIOS byte we are interested in
  1490. ;
  1491. BIOSDATA  SEGMENT   AT 40H    ;Describe where BIOS keeps his data
  1492.           ORG       10H       ;Skip parts we are not interested in
  1493. EQUIP     DB        ?         ;Equipment flag location
  1494. MONO      EQU       00110000B ;These bits on if monochrome
  1495. COLOR     EQU       11101111B ;Mask to make BIOS think of the color board
  1496. BIOSDATA  ENDS                ;End of interesting part
  1497. ;
  1498. ;         Next, describe some values for interrupts and functions
  1499. ;
  1500. DOS       EQU       21H       ;DOS Function Handler INT code
  1501. PRTMSG    EQU       09H       ;Function code to print a message
  1502. KBD       EQU       16H       ;BIOS keyboard services INT code
  1503. GETKEY    EQU       00H       ;Function code to read a character
  1504. SCREEN    EQU       10H       ;BIOS Screen services INT code
  1505. MONOINIT  EQU       02H       ;Value to initialize monochrome screen
  1506.  
  1507.  
  1508. IBM PC Assembly Language Tutorial                                        25
  1509.  
  1510.  
  1511. ;COLORINIT EQU       03H       ;Value to initialize color screen (80x25)
  1512. COLORINIT EQU      01H       ;Value to initialize color screen (40X25)
  1513. ;
  1514. ;         Now, describe our own segment
  1515. ;
  1516. SETSCRN   SEGMENT             ;Set operating segment for CODE and DATA
  1517. ;
  1518.           ASSUME CS:SETSCRN,DS:SETSCRN,ES:SETSCRN,SS:SETSCRN    ;All segments
  1519. ;
  1520.           ORG       100H      ;Begin assembly at standard .COM offset
  1521. ;
  1522. MAIN      PROC      NEAR      ;COM files use NEAR linkage
  1523.           JMP       BEGIN     ;And, it is helpful to put the data first, but
  1524. ;                             ;then you must branch around it.
  1525. ;
  1526. ;         Data used in SETSCRN
  1527. ;
  1528. CHANGELOC   DD      EQUIP     ;Location of the EQUIP, recorded as far pointer
  1529. MONOPROMPT  DB      'Please press the plus ( + ) key.$'    ;User sees on mono
  1530. COLORPROMPT DB      'Please press the minus ( - ) key.$'   ;User sees on color
  1531.  
  1532.  
  1533. Several things are illustrated on this page.  First, in addition to titles,
  1534. the assembler supports subtitles:  hence the SUBTTL pseudo-op.  Second, the
  1535. PAGE pseudo-op can be used to go to a new page in the listing.  You see an
  1536. example here of the DSECT-style segment in the "SEGMENT AT 40H".  Here, our
  1537. our interest is in correctly describing the location of some data in the
  1538. BIOS work area which really is located at segment 40H.
  1539.  
  1540. You will also see illustrated the EQU instruction, which just gives a sym-
  1541. bolic name to a number.  I don't make a fetish of giving a name to every
  1542. single number in a program.  I do feel strongly, though, that interrupts
  1543. and function codes, where the number is arbitrary and the function being
  1544. performed is the thing of interest, should always be given symbolic names.
  1545.  
  1546. One last new element in this section is the define doubleword (DD) instruc-
  1547. tion.  A doubleword constant can refer, as in this case, to a location in
  1548. another segment.  The assembler will be happy to use information at its
  1549. disposal to properly assemble it.  In this case, the assembler knows that
  1550. EQUIP is offset 10 in the segment BIOSDATA which is at 40H.
  1551.  
  1552.           SUBTTL -- Perform function
  1553.           PAGE
  1554. BEGIN:    CALL      MONOON                  ;Turn on mono display
  1555.           MOV       DX,OFFSET MONOPROMPT          ;GET MONO PROMPT
  1556.           MOV       AH,PRTMSG                     ;ISSUE
  1557.           INT       DOS                           ;IT
  1558.           CALL      COLORON                 ;Turn on color display
  1559.           MOV       DX,OFFSET COLORPROMPT         ;GET COLOR PROMPT
  1560.           MOV       AH,PRTMSG                     ;ISSUE
  1561.           INT       DOS                           ;IT
  1562.           MOV       AH,GETKEY               ;Obtain user response
  1563.           INT       KBD
  1564.           CMP       AL,'+'                  ;Does he want MONO?
  1565.           JNZ       NOMONO
  1566.  
  1567.  
  1568. IBM PC Assembly Language Tutorial                                        26
  1569.  
  1570.  
  1571.           CALL      MONOON                  ;yes.  give it to him
  1572. NOMONO:   RET
  1573. MAIN      ENDP
  1574.  
  1575.  
  1576. The main code section makes use of subroutines to keep the basic flow sim-
  1577. ple.  About all that's new to you in this section is the use of the BIOS
  1578. interrupt KBD to read a character from the keyboard.
  1579.  
  1580. Now for the subroutines, MONOON and COLORON:
  1581.  
  1582.           SUBTTL -- Routines to turn monitors on
  1583.           PAGE
  1584. MONOON    PROC      NEAR                ;Turn mono on
  1585.           LES       DI,CHANGELOC        ;Get location to change
  1586.           ASSUME    ES:BIOSDATA         ;TELL ASSEMBLER ABOUT CHANGE TO ES
  1587.           OR        EQUIP,MONO
  1588.           MOV       AX,MONOINIT         ;Get screen initialization value
  1589.           INT       SCREEN              ;Initialize screen
  1590.           RET
  1591. MONOON    ENDP
  1592. COLORON   PROC      NEAR                ;Turn color on
  1593.           LES       DI,CHANGELOC        ;Get location to change
  1594.           ASSUME    ES:BIOSDATA         ;TELL ASSEMBLER ABOUT CHANGE TO ES
  1595.           AND       EQUIP,COLOR
  1596.           MOV       AX,COLORINIT        ;Get screen initialization value
  1597.           INT       SCREEN              ;Initialize screen
  1598.           RET
  1599. COLORON   ENDP
  1600. SETSCRN   ENDS                          ;End of segment
  1601.           END       MAIN                ;End of assembly; execution at MAIN
  1602.  
  1603.  
  1604. The instructions LES and LDS are useful ones for dealing with doubleword
  1605. addresses.  The offset is loaded into the operand register and the segment
  1606. into ES (for LES) or DS (for LDS).  By telling the assembler, with an
  1607. ASSUME, that ES now addresses the BIOSDATA segment, it is able to correctly
  1608. assemble the OR and AND instructions which refer to the EQUIP byte.  An ES
  1609. segment prefix is added.
  1610.  
  1611. To understand the action here, you simply need to know that flags in that
  1612. particular byte control how the BIOS screen service initializes the adapt-
  1613. ers.  BIOS will only work with one adapter at a time; by setting the equip-
  1614. ment flags to show one or the other as installed and calling BIOS screen
  1615. initialization, we achieve the desired effect.
  1616.  
  1617. The rest is up to you.
  1618.  
  1619.  
  1620.  
  1621.  
  1622.  
  1623.  
  1624.  
  1625.  
  1626.  
  1627.  
  1628. IBM PC Assembly Language Tutorial                                        27
  1629.  
  1630.          ----------------end-of-author's-documentation---------------
  1631.  
  1632.                          Software Library Information:
  1633.  
  1634.                     This disk copy provided as a service of
  1635.  
  1636.                            Public (software) Library
  1637.  
  1638.          We are not the authors of this program, nor are we associated
  1639.          with the author in any way other than as a distributor of the
  1640.          program in accordance with the author's terms of distribution.
  1641.  
  1642.          Please direct shareware payments and specific questions about
  1643.          this program to the author of the program, whose name appears
  1644.          elsewhere in  this documentation. If you have trouble getting
  1645.          in touch with the author,  we will do whatever we can to help
  1646.          you with your questions. All programs have been tested and do
  1647.          run.  To report problems,  please use the form that is in the
  1648.          file PROBLEM.DOC on many of our disks or in other written for-
  1649.          mat with screen printouts, if possible.  PsL cannot debug pro-
  1650.          programs over the telephone, though we can answer questions.
  1651.  
  1652.          Disks in the PsL are updated  monthly,  so if you did not get
  1653.          this disk directly from the PsL, you should be aware that the
  1654.          files in this set may no longer be the current versions. Also,
  1655.          if you got this disk from another vendor and are having prob-
  1656.          lems,  be aware that  some files may have become corrupted or
  1657.          lost by that vendor. Get a current, working disk from PsL.
  1658.  
  1659.          For a copy of the latest monthly software library newsletter
  1660.          and a list of the 3,000+ disks in the library, call or write
  1661.  
  1662.                            Public (software) Library
  1663.                                P.O.Box 35705 - F
  1664.                             Houston, TX 77235-5705
  1665.  
  1666.                                  Orders only:
  1667.                                 1-800-2424-PSL
  1668.                               MC/Visa/AmEx/Discover
  1669.  
  1670.                           Outside of U.S. or in Texas
  1671.                           or for general information,
  1672.                               Call 1-713-524-6394
  1673.  
  1674.                           PsL also has an outstanding
  1675.                           catalog for the Macintosh.
  1676.  
  1677.  
  1678.